<?php

namespace app\common\library\addon;

use app\admin\model\AuthRule;
use app\common\library\Menu;
use app\common\library\MenuSite;
use app\common\model\Addons;
use app\site\model\SiteRule;
use PhpZip\ZipFile;
use think\facade\Db;
use ZipStream\ZipStream;

class Addon
{
    public static $excludedMethods = [
//        'index',
        '_initialize',
        'json_success',
        'json_error',
        '__construct',
        'recyclebin',
//        'add',
//        'edit',
//        'del',
//        'destroy',
//        'restore',
//        'multi',
    ];
    /**
     * 创建插件
     * @param string $addon 插件名称
     * @param string $type 插件类型
     * @param string $title 插件标题
     * @param string $intro 插件介绍
     * @param string $author 插件作者
     * @param string|int $website 插件作者官网
     * @param string $autorouter 是否开启自动路由
     * @param string $version 插件版本
     * @param string|int $state 插件状态
     * @param string|array $require 依赖的插件
     * @param array $event 其他参数
     * @return void
     */
    
    public static $init = [
        "{__NAME__}",
        "{__TITLE__}",
        "{__INTRO__}",
        "{__AUTHOR__}",
        "{__WEBSITE__}",
        "{__AUTOROUTER__}",
        "{__VERSION__}",
        "{__STATE__}",
        "{__URL__}",
        "{__REQUIRE__}",
    ];

    /**
     * 创建插件
     * @param string $addon
     * @param string $type
     * @param string $title
     * @param string $intro
     * @param string $author
     * @param string|int $website
     * @param string|int $autorouter
     * @param string $version
     * @param string|int $state
     * @param string|array $require
     * @param array $event
     * @return true
     * @throws \Exception
     */
    public static function createAddons(string $addon,string $type='addons',string $title='',string $intro='',string $author='',string|int $website='',string|int $autorouter=1,string $version='1.0.0',string|int $state=0,string|array $require="",array $event = [])
    {
        // 检测插件名称
        self::checkAddon($addon);
        // 检测依赖的插件
        if(!empty($require)){
            self::checkRequire($require);
        }
        if(is_array($require)){
            $require = implode(',',$require);
        }
        if($type=='addons'){
            $formDir = root_path().'app/common/library/addon/stubs/addons';
            $toDir = root_path().'addons/'.$addon;
        }else{
            $formDir = root_path().'app/common/library/addon/stubs/app';
            $toDir = root_path().'app/'.$addon;
        }
        if(!is_dir($formDir)){
            throw new \Exception('插件模板不存在');
        }
        if(!is_dir($toDir)){
            mkdir($toDir,0777,true);
        }
        // 复制文件
        self::copyFiles($formDir,$toDir);


        // 替换配置文件内容
        $initConfig = [
            $addon,
            $title,
            $intro,
            $author,
            $website,
            $autorouter,
            $version,
            $state,
            $url??"",
            $require,
            $event
        ];
        $newIni = [];
        foreach (self::$init as $key=>$value){
            if(isset($initConfig[$key])){
                $newIni[$value] = $initConfig[$key];
            }
        }
        $initDir = $toDir.'/info.ini';
        if(!file_exists($initDir)){
            throw new \Exception('插件配置文件不存在');
        }
        $str = file_get_contents($initDir);
        foreach ($newIni as $key=>$value){
            $str = str_replace($key,$value,$str);
        }
        file_put_contents($initDir,$str);


       if($type==='addons'){
           // 重命名文件
           $oldFileName = $toDir.'/Test.php';
           $newFileName = $toDir.'/'.ucfirst($addon).'.php';
           if($oldFileName!==$newFileName){
               rename($oldFileName,$newFileName);
           }
           $dirName = $toDir.'/public/assets/addons/test';
           $newDirName = $toDir.'/public/assets/addons/'.$addon;
           if(is_dir($dirName)){
               rename($dirName,$newDirName);
           }


           $str = file_get_contents($newFileName);
           // 替换类名
           $str = str_replace('Test',ucfirst($addon),$str);
           file_put_contents($newFileName,$str);
       }else{
           // 重命名文件
           $oldFileName = $toDir.'/Index.php';
           $newFileName = $toDir.'/'.ucfirst($addon).'.php';
           if($oldFileName!==$newFileName){
               rename($oldFileName,$newFileName);
           }
//           $dirName = $toDir.'/public/assets/addons/test';
//           $newDirName = $toDir.'/public/assets/addons/'.$addon;
//           if(is_dir($dirName)){
//               rename($dirName,$newDirName);
//           }
//
//
           $str = file_get_contents($newFileName);
           // 替换类名
           $str = str_replace('Index',ucfirst($addon),$str);
           file_put_contents($newFileName,$str);
       }



        if($type=='addons'){
            //处理命名空间
            $namespace = "namespace addons\\test";
            $newNamespace = "namespace addons\\".$addon;
        }else{
            //处理命名空间
            $namespace = "namespace app\\index";
            $newNamespace = "namespace app\\".$addon;
        }
        self::handNamespace($toDir,$namespace,$newNamespace);

        self::dbupdate($addon,$type);

        cleanAddonsCache();
        return true;
    }
    public static function del($addon)
    {
        $has = Addons::where('name',$addon)
            ->find();
        if(!empty($has)){
            $has->delete();
        }
    }
    // 保存插件信息
    public static function dbupdate($addon,$type='addons')
    {
        if($type=='addons'){
            // 保存插件信息
            $addonConfig = get_addons_info($addon);
        }else{
            $addonConfig = get_app_info($addon);
        }
        $has = Addons::where('name',$addon)
            ->find();
        if(empty($has)){
            Addons::create($addonConfig);
        }else{
            $has->save($addonConfig);
        }
    }
    /**
     * 处理命名空间
     * @param $initDir
     * @param $oldNamespace
     * @param $newNamespace
     * @return void
     */
    protected static function handNamespace($initDir,$oldNamespace,$newNamespace)
    {
        get_dir_hand($initDir,function ($dir)use($oldNamespace,$newNamespace){
            $str = file_get_contents($dir);
            $str = str_replace($oldNamespace,$newNamespace,$str);
            file_put_contents($dir,$str);
        });
    }
    // 复制文件
    protected static function copyFiles($formDir,$toDir)
    {
        copyDir($formDir,$toDir);
        return true;
    }

    /**
     *
     * @param $require
     * @return array|false|mixed|string[]
     * @throws \Exception
     */
    protected static function checkRequire($require)
    {
        if(!empty($require)){
            if(is_string($require)){
                $require = explode(',',$require);
            }
            $addonsList = get_addons_list();
            foreach ($require as $value){
                if(!in_array($value,$addonsList)){
                    throw new \Exception('依赖插件'.$value.'不存在');
                }
            }
        }else{
            $require = [];
        }
        return $require;

    }

    /**
     * 检测插件名称
     * @param $addon
     * @return true
     * @throws \Exception
     */
    protected static function checkAddon($addon)
    {
        if(empty($addon)){
            throw new \Exception('插件名称不能为空');
        }

        $res = preg_match('/^[A-Za-z_]+$/', $addon);
        if(empty($res)){
            throw new \Exception('插件名称只能是英文字母和下划线');
        }
        $addonsList = get_addons_list(true);
        if(in_array($addon,$addonsList)){
            throw new \Exception('插件已存在');
        }
        return true;
    }

    /**
     * 执行sql
     * @param $sqlPath
     * @return true
     */
    public static function runSql($sqlPath)
    {
        if(is_file($sqlPath)){
            $dbprefix = get_db_config('PREFIX');
            $get_sql_data = file_get_contents($sqlPath);
            if(!empty($get_sql_data)){
                $get_sql_data = str_replace("__PREFIX__", $dbprefix, $get_sql_data);
                $sql_query = self::getSqlQuery($get_sql_data);
                $query_count = count($sql_query);
                $sql_item = [];
                for ($i = 0; $i < $query_count; $i++) {
                    $sql = trim($sql_query[ $i ]);
                    if (str_contains($sql, 'CREATE TABLE')) {
                        $match_item = preg_match('/CREATE TABLE [`]?(\\w+)[`]?/is', $sql, $match_data);
                    } elseif (str_contains($sql, 'ALTER TABLE')) {
                        $match_item = preg_match('/ALTER TABLE [`]?(\\w+)[`]?/is', $sql, $match_data);

                    } elseif (str_contains($sql, 'INSERT INTO')) {
                        $match_item = preg_match('/INSERT INTO [`]?(\\w+)[`]?/is', $sql, $match_data);
                    } else {
                        $match_item = 0;
                    }
                    if ($match_item > 0) {
                        try {
                            $table_name = $match_data[ 1 ];
                            $new_table_name = $table_name;
                            $sql_item[] = self::str_replace_first($table_name, $new_table_name, $sql);
                        } catch (\Exception $e) {

                        }
                    }
                }
                foreach ($sql_item as $sql){
                    Db::execute($sql);
                }
            }
        }
        return true;
    }
    public static function getSqlQuery($sql_data)
    {
//        $this->checkLock();
        $sql_data = preg_replace("/TYPE=(InnoDB|MyISAM|MEMORY)( DEFAULT CHARSET=[^; ]+)?/", "ENGINE=\\1 DEFAULT CHARSET=utf8", $sql_data);

        $sql_data = str_replace("\r", "\n", $sql_data);
        $sql_query = [];
        $num = 0;
        $sql_arr = explode(";\n", trim($sql_data));
        unset($sql);
        foreach ($sql_arr as $sql) {
            $sql_query[ $num ] = '';
            $sqls = explode("\n", trim($sql));
            $sqls = array_filter($sqls);
            foreach ($sqls as $query) {
                $str1 = $query[ 0 ] ?? '';
                if ($str1 != '#' && $str1 != '-')
                    $sql_query[ $num ] .= $query;
            }
            $num++;
        }
        return $sql_query;
    }
    public static function str_replace_first($search, $replace, $subject)
    {
        return implode($replace, explode($search, $subject, 2));
    }

    /**
     * 安装插件
     * @param string $filePath zip包地址
     * @return void
     */
    public static function install($filePath)
    {
        if(!is_file($filePath)){
            throw new \Exception('文件不存在');
        }
        $tempDir = root_path().'runtime/temp/';
        self::delDir($tempDir);
        $zipFile = new \PhpZip\ZipFile();
        $zipFile->openFile($filePath);
//        $listFiles = $zipFile->getListFiles();
        if(!is_dir($tempDir)){
            mkdir($tempDir,0777,true);
        }
        $zipFile->extractTo($tempDir);
        $initFile = $tempDir.'/info.ini';
        if(!is_file($initFile)){
            throw new \Exception('插件配置文件不存在');
        }
        $config = parse_ini_file($initFile,true);
        if(empty($config)){
            throw new \Exception('插件配置文件错误');
        }
        if(empty($config['name'])){
            throw new \Exception('插件名称不能为空');
        }
        $addon = $config['name'];
        // 检测插件名称
        self::checkAddon($addon);
        // 检测依赖插件
        self::checkRequire($config['require']??"");
        if($config['type']=='addons'){
            $addonDir = root_path().'addons/'.$addon;
        }else{
            $addonDir = root_path().'app/'.$addon;
        }
        if(is_dir($addonDir)){
            throw new \Exception('插件已存在');
        }
        // 复制文件
        self::copyFiles($tempDir,$addonDir);

        //运行sql语句
        $sqlPath = $addonDir.'/install.sql';
        self::runSql($sqlPath);


        $en = ucfirst($addon);
        // 更新数据库
        self::dbupdate($addon,$config['type']);
        if($config['type']=='addons'){
            $className = "addons\\{$addon}\\{$en}";

        }else{
            $className = "app\\{$addon}\\{$en}";
        }
        if(class_exists($className)&&method_exists($className,'install')){
            $class = new $className();
            $class->install();
        }
        //删除临时文件
        self::delDir($tempDir);
        // 更新菜单
        $menuFile = $addonDir.'/menu.php';
        if(is_file($menuFile)){
            $menu = require $menuFile;
            if(!empty($menu)){
                if(is_string($menu)){
                    $menu = json_decode($menu,true);
                }
                Menu::create($menu);
            }
        }
        $siteMenuFile = $addonDir.'/site_menu.php';
        if(is_file($siteMenuFile)){
            $siteMenu = require $siteMenuFile;
            if(!empty($siteMenu)){
                if(is_string($siteMenu)){
                    $siteMenu = json_decode($siteMenu,true);
                }
                $res = MenuSite::getHasTable();
                if($res){
                    MenuSite::create($siteMenu);
                }

            }
        }

        // 启用插件
        self::enable($addon);
        cleanAddonsCache();
        return true;
    }

    /**
     * 卸载插件
     * @param $addon
     * @return void
     */
    public static function uninstall($addon)
    {
        $addonInfo = Addons::where('name',$addon)->find();
        if(empty($addonInfo)){
           throw new \Exception('插件不存在');
        }
        self::delCheckRequire($addon);
        if($addonInfo->type=='addons'){
            $config = get_addons_info($addon);
        }else{
            $config = get_app_info($addon);
        }
        if($config['state']==1){
            throw new \Exception('插件已启用,请先禁用');
        }
        if($addonInfo->type=='addons'){
            $addonDir = root_path().'addons/'.$addon;
        }else{
            $addonDir = root_path().'app/'.$addon;
        }

        if(is_dir($addonDir)){
            $temName = ucfirst($addon);
            $class = null;
            if($addonInfo->type=='addons'){
                $className = "addons\\{$addon}\\{$temName}";
            }else{
                $className = "app\\{$addon}\\{$temName}";
            }
            if(class_exists($className)&&method_exists($className,'uninstall')){
                $class = new $className();
                $class->uninstall('start');
            }
            //运行sql语句
            $sqlFile = $addonDir.'/uninstall.sql';
            if(is_file($sqlFile)){
                self::runSql($sqlFile);
            }
            //删除数据库
            $tables = self::getTables($addon);
            self::delTables($tables);

            //删除插件文件
            self::delDir($addonDir);
        }
        self::del($addon);
        if($addonInfo->type=='addons'){
            Menu::delete('addons/'.$addon);
        }else{
            Menu::delete($addon);
        }
        self::deleteSiteMenu($addon,$addonInfo->type);
        if(!empty($class)){
            $class->uninstall('end');
        }

        cleanAddonsCache();
        return true;
    }
    protected static function deleteSiteMenu($addon,$type)
    {
        $res = MenuSite::getHasTable();
        if($res){
            if($type=='addons'){
                MenuSite::delete('addons/'.$addon);
            }elseif ($type=='app'){
                MenuSite::delete($addon);
            }
        }
    }
    public static function delTables($tables)
    {
        if(!empty($tables)){
            foreach ($tables as $table){
                Db::execute("DROP TABLE IF EXISTS `{$table}`");
            }
        }
    }
    //获取表名
    public static function getTables($addon)
    {
        $dbprefix = get_db_config('PREFIX');
        $tables = Db::getTables();
        $dbprefix = $dbprefix.$addon;
        $table_names = [];
        foreach ($tables as $table){
            if(strpos($table,$dbprefix)===0){
                $table_names[] = $table;
            }
        }
        return $table_names;
    }

    /**
     * 检查依赖关系
     * @param $addon
     * @return true
     * @throws \Exception
     */
    protected static function delCheckRequire($addon)
    {
        $addonsList = get_addons_list(true);
        foreach ($addonsList as $value){
            if($value!=$addon){
                $config = get_addons_info($value);
                if(!empty($config['require'])){
                    $require = explode(',',$config['require']);
                    if(in_array($addon,$require)){
                        throw new \Exception('插件'.$value.'依赖插件'.$addon.'，请先卸载'.$value);
                    }
                }
            }
        }
        return true;
    }
    protected static function enableRootPath($addon,$type)
    {
        $fileDir = '';
        if($type=='addons'){
            $fileDir = root_path().'addons/'.$addon.'/public_path/public_path.php';
        }else{
            $fileDir = root_path().'app/'.$addon.'/public_path/public_path.php';
        }
        if(!is_file($fileDir)){
            return true;
        }
        $fileAlls = require $fileDir;
        foreach ($fileAlls as $key=>$value){
            if($type=='addons'){
                $filePath = root_path().'addons/'.$addon.'/public_path/'.$value;
            }else{
                $filePath = root_path().'app/'.$addon.'/public_path/'.$value;
            }

            if(is_file($filePath)){
                copy($filePath,root_path().'public/'.$value);
                if(config('fastadmin.addon_pure_mode')){
                    @unlink($filePath);
                }
            }elseif(is_dir($filePath)){
                copyDir($filePath,root_path().'public/'.$value);
                if(config('fastadmin.addon_pure_mode')){
                    self::delDir($filePath);
                }
            }
        }
        return true;
    }
    //disable
    protected static function disableRootPath($addon,$type)
    {
        $fileDir = '';
        if($type=='addons'){
            $fileDir = root_path().'addons/'.$addon.'/public_path/public_path.php';
        }else{
            $fileDir = root_path().'app/'.$addon.'/public_path/public_path.php';
        }
        if(!is_file($fileDir)){
            return true;
        }
        $fileAlls = require $fileDir;
        foreach ($fileAlls as $key=>$value){
            $filePath = root_path().'public/'.$value;
            if($type=='addons'){
                $addonFilePath = root_path().'addons/'.$addon.'/public_path/'.$value;
            }else{
                $addonFilePath = root_path().'app/'.$addon.'/public_path/'.$value;
            }

            if(is_file($filePath)){
                copy($filePath,$addonFilePath);
                if(config('fastadmin.addon_pure_mode')){
                    @unlink($filePath);
                }
            }elseif(is_dir($filePath)){
                copyDir($filePath,$addonFilePath);
                if(config('fastadmin.addon_pure_mode')){
                    self::delDir($filePath);
                }
            }
        }
        return true;
    }
    /**
     * 启用插件
     * @param $addon
     * @return void
     */
    public static function enable($addon)
    {
        $addonInfo = Addons::where('name',$addon)->find();
        if(empty($addonInfo)){
            throw new \Exception('插件不存在');
        }
        $addName = ucfirst($addon);
        if($addonInfo->type=='addons'){
            $class = "addons\\".$addon."\\".$addName;
        }else{
            $class = "app\\".$addon."\\".$addName;
        }

        $obj = null;
        if(method_exists($class,'enable')){
            $obj = new $class;
            $obj->enable('start');
        }
        if($addonInfo->type=='addons'){
            $dir = root_path().'addons/'.$addon;
        }else{
            $dir = root_path().'app/'.$addon;
        }
        //静态文件
        $rootdir = $dir.'/root';
        $files = $dir.'/public/public.php';
        if(is_file($files)){
            $fileArray = require $files;
            if(!empty($fileArray)){
                $baseDir = $dir.'/public/';
                $publicDir = root_path().'public/';
                foreach ($fileArray as $key=>$value){
                    $base_tem_path = $baseDir.$value;
                    $public_tem_path = $publicDir.$value;
                    if(is_file($base_tem_path)){
                        copy($base_tem_path,$public_tem_path);
                    }elseif(is_dir($base_tem_path)){
                        self::copyFiles($base_tem_path,$public_tem_path);
                    }
                    if(config('fastadmin.addon_pure_mode')){
                        self::delDir($base_tem_path);
                    }
                }
            }
        }
//        if(is_dir($rootdir)){
//            $dirs = scandir($rootdir);
//            $newdir = [];
//            foreach ($dirs as $value){
//                if($value!='.'&&$value!='..'){
//                    $newdir[] = $value;
//                }
//            }
////         复制文件
//            if(!empty($newdir)){
//                if($addonInfo->type=='addons'){
//                    $nasePath = root_path().'public/addons/'.$addon.'/';
//                }else{
//                    $nasePath = root_path().'public/assets/js/'.$addon.'/';
//                }
//
//                if(!is_dir($nasePath)){
//                    mkdir($nasePath,0777,true);
//                }
//                foreach ($newdir as $value){
//                    if(is_file($rootdir.'/'.$value)){
//                        copy($rootdir.'/'.$value,$nasePath.$value);
//                    }elseif (is_dir($rootdir.'/'.$value)){
//                        self::copyFiles($rootdir.'/'.$value,$nasePath.$value);
//                    }
//                }
//            }
//            //纯净模式
//            if(config('fastadmin.addon_pure_mode')){
//                self::delDir($rootdir);
//            }
//        }



        //修改配置文件
        $initDir = $dir.'/info.ini';
        $config = parse_ini_file($initDir);
        $config['state'] = 1;
        $str = "";
        foreach ($config as $key=>$value){
            $str .= $key." = ".$value.PHP_EOL;
        }
        file_put_contents($initDir,$str);
        self::dbupdate($addon,$addonInfo->type);
        if($addonInfo->type=='addons'){
            Menu::enable('addons/'.$addon);
        }else{
            Menu::enable($addon);
        }
        if(!empty($obj)){
            $obj->enable('end');
        }
        //启用根目录文件
        self::enableRootPath($addon,$addonInfo->type);
        self::enabledSiteMenu($addon,$addonInfo->type);
        cleanAddonsCache();
        return true;
    }
    protected static function enabledSiteMenu($addon,$type)
    {
        if($type=='app'){
            MenuSite::enable($addon);
        }elseif ($type=='addons'){
            MenuSite::enable('addons/'.$addon);
        }
    }
    protected static function delDir($path)
    {
        if(is_dir($path)){
            $dirs = scandir($path);
            foreach ($dirs as $value){
                if($value!='.'&&$value!='..'){
                    self::delDir($path.'/'.$value);
                }
            }
            @rmdir($path);
        }else{
            @unlink($path);
        }
        return true;
    }
    /**
     * 禁用插件
     * @param $addon
     * @return void
     */
    public static function disable($addon)
    {
        $addonInfo = Addons::where('name',$addon)->find();
        if(empty($addonInfo)){
            throw new \Exception('插件不存在');
        }
//        self::delCheckRequire($addon);
        if($addonInfo->type=='addons'){
            $class = "addons\\".$addon."\\".ucfirst($addon);
        }else{
            $class = "app\\".$addon."\\".ucfirst($addon);
        }
        $obj = null;
        if(method_exists($class,'disable')){
            $obj = new $class;
            $obj->disable('start');
        }
        if($addonInfo->type=='addons'){
            $arrayDir = root_path().'addons/'.$addon.'/public/public.php';
            $baseDir = root_path().'addons/'.$addon.'/public/';
            $publicDir = root_path().'public/';
            if(is_file($arrayDir)){
                $arrayFiles = require $arrayDir;
                if(!empty($arrayFiles)){
                    foreach ($arrayFiles as $key=>$value){
                        $temp_public_Dir = $publicDir.$value;
                        $base_public_dir = $baseDir.$value;
                        if(is_file($temp_public_Dir)){
                            copy($temp_public_Dir,$base_public_dir);
                        }elseif (is_dir($temp_public_Dir)){
                            self::copyFiles($temp_public_Dir,$base_public_dir);
                        }
                        self::delDir($temp_public_Dir);
                    }
                }
            }
            $addonDir = root_path().'addons/'.$addon.'/';
//            //迁移静态文件
//            $staticDir = root_path().'public/addons/'.$addon;
//            if(is_dir($staticDir)){
//                $addonStaticDir = $addonDir.'/root';
//                if(!is_dir($addonStaticDir)){
//                    mkdir($addonStaticDir,0777,true);
//                }
//                self::copyFiles($staticDir,$addonStaticDir);
//                self::delDir($staticDir);
//            }
        }else{
            $addonDir = root_path().'app/'.$addon.'/';
            $arrayDir = root_path().'app/'.$addon.'/public/public.php';
            $baseDir = root_path().'app/'.$addon.'/public/';
            $publicDir = root_path().'public/';
            if(is_file($arrayDir)){
                $arrayFiles = require $arrayDir;
                foreach ($arrayFiles as $key=>$value){
                    $temp_public_Dir = $publicDir.$value;
//                    echo "<pre>";
//                    print_r($temp_public_Dir);
//                    exit;
                    $base_public_dir = $baseDir.$value;
                    if(is_file($temp_public_Dir)){
                        copy($temp_public_Dir,$base_public_dir);
                    }elseif (is_dir($temp_public_Dir)){
                        self::copyFiles($temp_public_Dir,$base_public_dir);
                    }
                    self::delDir($temp_public_Dir);
                }
            }
        }



        //修改配置文件
        $initDir = $addonDir.'/info.ini';
        $config = parse_ini_file($initDir);
        $config['state'] = 0;
        $str = "";
        foreach ($config as $key=>$value){
            $str .= $key." = ".$value.PHP_EOL;
        }
        file_put_contents($initDir,$str);
        self::dbupdate($addon,$addonInfo->type);
        if($addonInfo->type=='addons'){
            Menu::disable('addons/'.$addon);
        }else{
            Menu::disable($addon);
        }
        if(!empty($obj)){
            $obj->disable('end');
        }
        //禁用根目录文件
        self::disableRootPath($addon,$addonInfo->type);
        self::disableSiteMenu($addon,$addonInfo->type);
        cleanAddonsCache();
        return true;
    }
    protected static function disableSiteMenu($addon,$type)
    {
        $appList = get_app_list(true);
        if(in_array('site',$appList)){
            if($type=='app'){
                MenuSite::disable($addon);
            }elseif($type=='addons'){
                $dir = root_path()."addons/{$addon}/app/site";
                if(is_dir($dir)){
                    MenuSite::disable('addons/'.$addon);
                }
            }
        }
    }

    /**
     * 升级插件
     * @param $addon
     * @return void
     */
    public static function upgrade($addon,$filePath='')
    {
        if(!is_file($filePath)){
            throw new \Exception('文件不存在');
        }
        //触发插件升级
        $class = "addons\\".$addon."\\".ucfirst($addon);
        $obj = null;
        if(method_exists($class,'upgrade')){
            $obj = new $class;
            $obj->upgrade('start');
        }
//        $filePath = root_path().'runtime/addons/'.$addon.'1.0.1.zip';

        //先禁用插件
        self::disable($addon);
        $t_dir = root_path().'runtime/tem/';
        $temDir = $t_dir.$addon;
        $zipFile = new \PhpZip\ZipFile();
        $zipFile->openFile($filePath);
//        $listFiles = $zipFile->getListFiles();
        if(!is_dir($temDir)){
            mkdir($temDir,0777,true);
        }
        $zipFile->extractTo($temDir);
        $addonDir = root_path().'addons/'.$addon;
        //拷贝文件
        self::copyFiles($temDir,$addonDir);
        //运行sql
        $sqlFile = $addonDir.'/upgrade.sql';
        if(is_file($sqlFile)){
            self::runSql($sqlFile);
        }
        //删除临时文件
        self::delDir($t_dir);
        //启用插件
        self::enable($addon);
        self::dbupdate($addon);
        if(!empty($obj)){
            $obj->upgrade('end');
        }
        cleanAddonsCache();
        return true;
    }
    protected function decompression ($filePath)
    {
        $zip = new ZipFile();
        $zip->openFile($filePath);
//        $temDir = sys_get_temp_dir();
    }
    public static function pack($addon)
    {
        $addonInfo = Addons::where('name',$addon)->find();
        if(empty($addonInfo)){
            throw new \Exception('插件不存在');
        }
        //处理本地菜单
        if($addonInfo->type=='addons'){
            $addonDir = root_path().'addons/'.$addon;
            $iniconfig = $addonDir.'/info.ini';
            $config = parse_ini_file($iniconfig);
            if($config['state']==1){
                self::disable($addon);
            }

            $menu_file = $addonDir.'/menu.php';
            $menu_list = self::getMenus($addon,$addonInfo->type);
            if(!empty($menu_list)){
                $menu_str = json_encode256($menu_list);
                $stub_file = root_path().'app/common/library/addon/stubs/menu.stub';
                $str = file_get_contents($stub_file);
                $str = str_replace('{__MENU__}',$menu_str,$str);
                file_put_contents($menu_file,$str);
            }
        }else{
            $addonDir = root_path().'app/'.$addon;
            $iniconfig = $addonDir.'/info.ini';
            $config = parse_ini_file($iniconfig);
            if($config['state']==1){
                self::disable($addon);
            }
            $menu_file = $addonDir.'/menu.php';
            $menu_list = self::getMenus($addon,$addonInfo->type);
            $menu_str = json_encode256($menu_list);
            $stub_file = root_path().'app/common/library/addon/stubs/menu.stub';
            $str = file_get_contents($stub_file);
            $str = str_replace('{__MENU__}',$menu_str,$str);
            file_put_contents($menu_file,$str);
        }
        self::handSiteMenus($addon,$addonInfo->type);


        //替换sql文件
        $sqlFile = [
            $addonDir.'/upgrade.sql',
            $addonDir.'/uninstall.sql',
            $addonDir.'/install.sql'
        ];
        self::changeWz($sqlFile);
        $zip = new ZipFile();
//        $fiels = get_dir_path(dir:$addonDir,type:'all',has_child: true,is_path: true);
        $zip->addDirRecursive($addonDir);
        $tem_file_dir = root_path().'runtime/addons/';
        if(!is_dir($tem_file_dir)){
            mkdir($tem_file_dir,0777,true);
        }
        $tem_file_dir = $tem_file_dir.$config['title'].'-'.$addon.'-'.$config['version'].'.zip';
        $zip->saveAsFile($tem_file_dir);
        $zip->close();
        $tem_file_dir = str_replace('/',DIRECTORY_SEPARATOR,$tem_file_dir);
        return $tem_file_dir;
    }

    /**
     * 处理站点菜单
     * @param $addon
     * @param $type
     * @return void
     */
    protected static function handSiteMenus($addon,$type)
    {
        $res = MenuSite::getHasTable();
        if($res){
            if($type=='app'){
                $menu_list = self::getSiteMenus($addon);
            }elseif ($type=='addons'){
                $menu_list = self::getSiteMenus('addons/'.$addon);
            }
            if(!empty($menu_list)){
                if($type=='app'){
                    $addonDir = root_path().'app/'.$addon;
                }elseif($type=='addons'){
                    $addonDir = root_path().'addons/'.$addon;
                }
                $menu_file = $addonDir.'/site_menu.php';
                $menu_str = json_encode256($menu_list);
                $stub_file = root_path().'app/common/library/addon/stubs/menu.stub';
                $str = file_get_contents($stub_file);
                $str = str_replace('{__MENU__}',$menu_str,$str);
                file_put_contents($menu_file,$str);
            }

        }

    }
    protected static function getSiteMenus($addon)
    {
        $fields = [
            'id',
            'pid',
            'name',
            'module',
            'route',
            'title',
            'icon',
            'remark',
            'ismenu',
            'weigh',
            'status',
        ];
        $addon_menu = SiteRule::where('name','=',$addon)
            ->field($fields)->find();
        if(empty($addon_menu)){
            $addon_menu = SiteRule::where('module','=',$addon)
                ->order('id asc')
                ->field($fields)->find();
        }
        if(!empty($addon_menu)){
            $addon_menu = $addon_menu->toArray();
        }
        $list = SiteRule::where('module',$addon)
            ->field($fields)
            ->order('weigh','desc')
            ->order('id','asc')
            ->select()->toArray();
        if(!empty($list)){
            $list = list_to_tree($list,'id','pid','sublist',$addon_menu['id']);
            if(!empty($list)){
                $list = self::delField($list,['id','pid']);
            }
        }
        if(!empty($addon_menu)){
            unset($addon_menu['id']);
            unset($addon_menu['pid']);
            $addon_menu['sublist'] = $list;
            $list = [$addon_menu];
        }else{
            $list = [];
        }

        return $list;
    }
    protected static function getMenus($addon,$type)
    {
        $fields = [
            'id',
            'pid',
            'name',
            'module',
            'route',
            'title',
            'icon',
            'remark',
            'ismenu',
            'weigh',
            'status',
        ];
        if($type=='addons'){
            $addon = 'addons/'.$addon;
        }
        $list = AuthRule::where('id','>',0)
            ->field($fields)
            ->order('weigh','desc')
            ->order('id','asc')
            ->select()
            ->toArray();
        $addon_menu = AuthRule::where('name','like',$addon)
            ->field($fields)->find();
        if(!empty($addon_menu)){
            $addon_menu = $addon_menu->toArray();
            if(!empty($list)){
                $list = list_to_tree($list,'id','pid','sublist',$addon_menu['id']);
//            $list = array_merge($addon_menu,$list);
                if(!empty($list)){
                    $list = self::delField($list,['id','pid']);
                }
            }
            unset($addon_menu['id']);
            unset($addon_menu['pid']);
            $addon_menu['sublist'] = $list;
            $list = [$addon_menu];
        }else{
            $list = [];
        }
        return $list;
    }
    protected static function delField($list,$fields=[])
    {
        foreach ($list as $k=>$v){
            if(!empty($v['children'])){
                $v['children'] = self::delField($v['children'],$fields);
            }
            foreach ($fields as $field){
                unset($v[$field]);
            }
            $list[$k] = $v;
        }
        return $list;
    }
    protected static function changeWz(array $sqlList)
    {
        if(empty($sqlList)){
            return true;
        }
        $dbprefix = get_db_config('PREFIX');
        $repce = '__PREFIX__';
        $sqlList = array_map(function ($item) use ($dbprefix,$repce){
            if(is_file($item)){
                $str = file_get_contents($item);
                $str = str_replace($dbprefix,$repce,$str);
                file_put_contents($item,$str);
            }
        },$sqlList);
        return true;
    }

    /**
     * 菜单
     * @return void
     */
    public static function Menu()
    {

        //更新插件菜单
        self::updateAddonsMenu();
        //更新应用菜单
//        self::updateAppMenu();

    }

    /**
     * 更新应用菜单
     * @return void
     */
    protected static function updateAppMenu()
    {
        $dir = root_path().'app/admin/controller';
        $files = get_dir_path(dir:$dir,type:'file',is_path: true,excluded: ['.htaccess'],has_child: true);
        foreach ($files as $file){
            if($class = file_get_class($file)){
                $obj = new \ReflectionClass($class);
                $allMethod = $obj->getMethods(\ReflectionMethod::IS_PUBLIC);
                $allArray = [];
                foreach ($allMethod as $method){
                    if($method->class!=$class){
                        continue;
                    }
                    $allArray[] = $method->name;
                }
                $allArray = array_diff($allArray,self::$excludedMethods);
                if(!empty($allArray)){
                    foreach ($allArray as $methodName){
                        self::getMenuAppConfig($class, $methodName,$obj);
                    }

                }
            }
        }
    }
    /**
     * 更新菜单
     * @return void
     */
    protected static function updateAddonsMenu()
    {
        $list = get_state_addons(true);
        if(empty($list)){
            return true;
        }
        $dir = root_path().'addons/';
        foreach ($list as $k=>$v){
            $appDir = $dir.$v.'/app';
            if(is_dir($appDir)){
                $dirs = get_dir_path(dir:$appDir,type:'dir',is_path: false,excluded: ['.htaccess']);
//                $dirs = ['admin','api','index'];
                foreach ($dirs as $odir){
                    if($odir!='admin'){
                        continue;
                    }
                    $controllerDir = $appDir.'/'.$odir.'/controller';
                    if(is_dir($controllerDir)){
                        $files = get_dir_path(dir:$controllerDir,type:'file',is_path: true,excluded: ['.htaccess'],has_child: true,dd: true);
                        foreach ($files as $file){
                            if($class = file_get_class($file)){
                                $obj = new \ReflectionClass($class);
                                $allMethod = $obj->getMethods(\ReflectionMethod::IS_PUBLIC);
                                $allArray = [];
                                foreach ($allMethod as $method){
                                    if($method->class!=$class){
                                        continue;
                                    }
                                    $allArray[] = $method->name;
                                }
                                $allArray = array_diff($allArray,self::$excludedMethods);
                                if(!empty($allArray)){
                                    foreach ($allArray as $methodName){
                                        self::getMenuAddonsConfig($class, $methodName,$v,$obj);
                                    }

                                }
                            }
                        }
                    }
                }
            }
        }
        return true;
    }
    protected static function getMenuAddonsConfig($className, $methodName,$addon,$obj)
    {
        $module = 'addons/'.$addon;
        $rep = 'addons\\'.$addon.'\app\\';
        $route = str_replace('\\controller','',str_replace($rep,'',$className));
        $route = strtolower($route);
        $route = explode('\\',$route);
        $route[] = $methodName;
        $controllerArray =array_slice($route,1,-1);
        $controller = implode('.',$controllerArray);
        $router = $route[0].'/'.$controller.'/'.$route[count($route)-1];
        $has = AuthRule::where('route',$router)->find();
        $config = get_addons_info($addon);
        if(empty($has)){
            $upName = $module.'/'.$controller;
            $hasIn = AuthRule::where('name','addons/'.$addon)->find();
            if(empty($hasIn)){
                $hasIn =AuthRule::create([
                    'name'=>'addons/'.$addon,
                    'title'=>$config['title'],
                    'module'=>'addons',
                    'route'=>'',
                    'pid'=>0,
                    'ismenu'=>1,
                    'weigh'=>0,
                    'status'=>'1',
                    'remark'=>'',
                ]);
            }
            $upNameObj = AuthRule::where('name',$upName)->find();
            if(empty($upNameObj)){

                $upNameObj = AuthRule::create([
                    'title'=>self::getClassDocComment($obj),
                    'module'=>$module,
                    'name'=>$upName,
                    'route'=>$route[0].'/'.$controller,
                    'ismenu'=>1,
                    'pid'=>$hasIn['id']
                ]);
            }
            $mesld = [
                'title'=>self::getMethodDocComment($obj,$methodName),
                'module'=>$module,
                'name'=>$upName.'/'.$methodName,
                'route'=>$router,
                'ismenu'=>0,
                'pid'=>$upNameObj['id']
            ];
            AuthRule::create($mesld);
        }
        return true;
    }
    protected static function getClassDocComment($obj)
    {
        $comment = $obj->getDocComment();
        if(empty($comment)){
            $nameArray = explode('\\',$obj->getName());
            return ucfirst($nameArray[count($nameArray)-1]);
        }
        return self::getDocCommentArray($comment,$obj->getName());
    }
    protected static function getDocCommentArray($docComment,$name)
    {
        $comment = explode(PHP_EOL,$docComment);
        foreach ($comment as $k=>&$v){
            $v =trim($v,'/');
            $v = trim($v,' *');
//            $v = trim($v,'*');
        }
        $comment = array_values(array_filter($comment));
        if(isset($comment[0])){
            return $comment[0];
        }
        $nameArray = explode('\\',$name);
        return ucfirst($nameArray[count($nameArray)-1]);
    }
    protected static function getMethodDocComment($obj,$methodName)
    {
        $DocComment = $obj->getMethod($methodName)->getDocComment();
        if(empty($DocComment)){
            return ucfirst($methodName);
        }
        return self::getDocCommentArray($DocComment,$methodName);
    }
}